package com.biu.billiards.controller;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.biu.billiards.config.BaseErrorEnum;
import com.biu.billiards.config.BaseException;
import com.biu.billiards.model.entity.Goods;
import com.biu.billiards.model.entity.QR;
import com.biu.billiards.model.entity.Shop;
import com.biu.billiards.model.entity.WxPayV3Bean;
import com.biu.billiards.model.vo.jsApiPay.GetApiPlayVo;
import com.biu.billiards.model.vo.shop.orderAdmin.AddOrderAdminVo;
import com.biu.billiards.model.vo.wx.GetWxPhoneVo;
import com.biu.billiards.response.BaseResponse;
import com.biu.billiards.response.RespGenerator;
import com.biu.billiards.service.*;
import com.biu.billiards.utils.GetOpenIdUtil;
import com.biu.billiards.utils.WxQrCode;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.Payer;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import io.swagger.annotations.ApiOperation;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author xue
 * @since 2023-09-25
 */
@RestController
@RequestMapping()
public class WxController {

    private final Logger log = LoggerFactory.getLogger(this.getClass());


    @Resource
    private IOrderService orderService;

    @Resource
    private IBilliardsService billiardsService;



    @Resource
    private IShopService shopService;

    @Resource
    private IUserService userService;

    @Resource
    private IGoodsService goodsService;

    @Resource
    WxPayV3Bean wxPayV3Bean;

    String serialNo;


    @ApiOperation(value = "获取小程序二维码")
    @PostMapping("/getMiniQr")
    public BaseResponse<Map<String,Object>> getQRBase64(@RequestBody QR qr)  {
        System.out.println("qraaa");
        System.out.println(qr);

        return RespGenerator.returnOK(WxQrCode.getminiqrQr(qr.getScene(),qr.getWidth(),qr.getPath()));
    }

    @ApiOperation(value = "余额支付")
    @PostMapping(value = "/pay/balance")
    public BaseResponse<HashMap> payBalance(@RequestBody GetApiPlayVo getApiPlayVo) {


        return RespGenerator.returnOK(userService.payBalance(getApiPlayVo));

    }



    @ApiOperation(value = "支付方式")
    @PostMapping(value = "/pay/way")
    public BaseResponse<HashMap> payWay(@RequestBody GetApiPlayVo getApiPlayVo) {
        return RespGenerator.returnOK(userService.userBalanceLack(getApiPlayVo));
    }



    @ApiOperation(value = "微信小程序登录")
    @GetMapping(value = "/wx/login")
    public BaseResponse<HashMap> wxLogin(String code) {

        GetOpenIdUtil getOpenIdUtil=new GetOpenIdUtil();
        String jsonId = getOpenIdUtil.getOpenid("wx4f370209e3d17631",code,"41bde00c1e10a408d974ca09c7204853");
        JSONObject jsonObject = JSONObject.parseObject(jsonId);

        return RespGenerator.returnOK(jsonObject);

    }







    @ApiOperation(value = "获取手机号码")
    @PostMapping("/getPhoneNumber")
    public Map<String, Object> getPhoneNumber(@RequestBody GetWxPhoneVo getWxPhoneVo){

        Map<String, Object> map = new HashMap<>();
        // 被加密的数据
        byte[] dataByte = Base64.decode(getWxPhoneVo.getEncryptedData());
        // 加密秘钥
        byte[] keyByte = Base64.decode(getWxPhoneVo.getSessionKey());
        // 偏移量
        byte[] ivByte = Base64.decode(getWxPhoneVo.getIv());
        try {
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                JSONObject jsonObject = JSONObject.parseObject(result);
                map.put("code", 200);
                map.put("msg", "解密成功");
                map.put("data", jsonObject);

                System.out.println(map);
                return map;
            }else{
                map.put("code", 500);
                map.put("msg", "解密失败");
                map.put("data", "");
                return map;
            }
        } catch (Exception e) {
            map.put("code", 500);
            map.put("msg", "解密失败");
            map.put("data", "");
            e.printStackTrace();
        }

        return map;
    }



    private boolean addOrderTopUp(String out_trade_no,GetApiPlayVo getApiPlayVo,Integer type){


        AddOrderAdminVo addOrderVo = new AddOrderAdminVo();
        BigDecimal pay = null;

        if (type==1){
            pay=new BigDecimal(200);
            addOrderVo.setGoodsName("200元套餐");

        }else if (type==2){
            pay=new BigDecimal(500);
            addOrderVo.setGoodsName("500元套餐");
        }

        addOrderVo.setOrderType(2);
        addOrderVo.setOrderId(out_trade_no);
        addOrderVo.setPlayWay("充值");
        addOrderVo.setPayPrice(pay);

        addOrderVo.setOrderStatus(0);
        addOrderVo.setShopId(getApiPlayVo.getShopId());
        addOrderVo.setUserId(getApiPlayVo.getUserId());

        addOrderVo.setPhone(getApiPlayVo.getPhone());





        return orderService.addOrder(addOrderVo);


    }

    private boolean addOrder(String out_trade_no,GetApiPlayVo getApiPlayVo,Goods goods){

        //      添加记录

//        Order(orderId=98b9b13b04ec4b9685cbe372545e647e, userId=null,
//                playWay=微信支付, createTime=2023-10-24T10:35:25.335609,
//                couponName=null, couponPrice=null, actualPrice=17.99,
//                startTime=null, endTime=null, goodsPrice=17.99,
//                goodsName=null, orderStatus=0, userBalance=null, orderType=1, payPrice=null)


        int hours ;
        AddOrderAdminVo addOrderVo = new AddOrderAdminVo();
        addOrderVo.setOrderId(out_trade_no);
        addOrderVo.setPlayWay("微信支付");
        addOrderVo.setCreateTime(LocalDateTime.now());
        addOrderVo.setGoodsId(getApiPlayVo.getGoodsId());
        addOrderVo.setOrderStatus(0);




        addOrderVo.setUserId(getApiPlayVo.getUserId());
        addOrderVo.setShopId(getApiPlayVo.getShopId());


        addOrderVo.setOrderType(1);



        addOrderVo.setBilliardsNumber(getApiPlayVo.getBilliardsNumber());

        addOrderVo.setDeviceNumber(getApiPlayVo.getDeviceNumber());

        addOrderVo.setBilliardsId(getApiPlayVo.getBilliardsId());


        addOrderVo.setPhone(getApiPlayVo.getPhone());




        addOrderVo.setHardwareId(getApiPlayVo.getHardwareId());




        if (goods!=null){
            hours = goods.getPlayHour();
            addOrderVo.setGoodsName(goods.getGoodsName());
         //   addOrderVo.setCouponPrice(new BigDecimal(0));
         //   addOrderVo.setGoodsPrice( goods.getGoodsPrice());
            addOrderVo.setActualPrice( goods.getGoodsPrice());
            addOrderVo.setBilliardsHour(hours);


            Shop shop = shopService.getShop(getApiPlayVo.getShopId());

            if (shop!=null){

                addOrderVo.setRecommendId(shop.getRecommendId());
                addOrderVo.setFeeType(shop.getFeeType());

                if (getApiPlayVo.getFeeType()==1){


                    // 0.06+0.094
                    addOrderVo.setRecommendActualSum(new BigDecimal(0));
                    addOrderVo.setPlatformActualSum(new BigDecimal(0));
                    addOrderVo.setShopActualSum(goods.getGoodsPrice().multiply(new BigDecimal(0.994)));

                    shopService.addWallet(shop.getShopId(),goods.getGoodsPrice().multiply(new BigDecimal(0.994)));


                } else if (getApiPlayVo.getFeeType()==2) {


                    BigDecimal recommendActualSum = goods.getGoodsPrice().multiply(new BigDecimal(0.005964));

                    BigDecimal platformActualSum = goods.getGoodsPrice().multiply(new BigDecimal(0.023856));
               //     BigDecimal shopActualSum = goods.getGoodsPrice().subtract(recommendActualSum).subtract(platformActualSum);

                    BigDecimal shopActualSum = goods.getGoodsPrice().multiply(new BigDecimal(0.96418));

                    addOrderVo.setShopActualSum(shopActualSum);
                    addOrderVo.setRecommendActualSum(recommendActualSum);
                    addOrderVo.setPlatformActualSum(platformActualSum);

                    userService.addWallet(shop.getRecommendId(),recommendActualSum);

                    shopService.addWallet(shop.getShopId(),shopActualSum);


                }

            }




        }



        return orderService.addOrder(addOrderVo);


    }



    private int typeToTopUp(Integer type){


        if (type==1){
            return 20000;
        }else if (type==2){
            return 50000;
        }

        return 20000;

    }

    private int typeToPrice(Goods goods){


      if (goods!=null){
          return goods.getGoodsPrice().multiply(new BigDecimal(100)).intValue();
      }else {
          return 99999;
      }


    }



    @PostMapping("/api/topUp")
    @ResponseBody
    public BaseResponse<Map<String,Object>> topUp(@RequestBody GetApiPlayVo getApiPlayVo) {

        System.out.println("wwasdasdasdasdasdasdasdasd");

        String out_trade_no = PayKit.generateStr();

        addOrderTopUp(out_trade_no,getApiPlayVo,getApiPlayVo.getPayType());

        int targetAmount = typeToTopUp(getApiPlayVo.getPayType());


        try {
            String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                    .setAppid(wxPayV3Bean.getAppId())
                    .setMchid(wxPayV3Bean.getMchId())
                    .setDescription("IJPay 让支付触手可及")
                    .setOut_trade_no(out_trade_no)
                    .setTime_expire(timeExpire)
                    .setAttach("充值")
                    .setNotify_url("https://kygtq.cn/payNotify")
                    .setAmount(new Amount().setTotal(targetAmount))
                    .setPayer(new Payer().setOpenid(getApiPlayVo.getOpenId()));


            //https://30jp902554.yicp.fun
            //https://kygtq.cn


            log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    BasePayApiEnum.JS_API_PAY.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(unifiedOrderModel)
            );


            log.info("统一下单响应 {}", response);
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
            log.info("verifySignature: {}", verifySignature);
            System.out.println(response.getStatus());
            if (response.getStatus() == 200 && verifySignature) {

                String body = response.getBody();
                cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(body);
                String prepayId = jsonObject.getStr("prepay_id");
                Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayV3Bean.getAppId(), prepayId, wxPayV3Bean.getKeyPath());
                log.info("唤起支付参数:{}", map);

                return RespGenerator.returnOK(map);

            }

            System.out.println(JSONUtil.toJsonStr(response));
            return RespGenerator.returnOK(response);

        } catch (Exception e) {
            e.printStackTrace();
            return RespGenerator.returnOK(e.getMessage());

        }




    }


    @PostMapping("/apiPay")
    @ResponseBody
    public BaseResponse<Map<String,Object>> apiPay(@RequestBody GetApiPlayVo getApiPlayVo) {


        if (billiardsService.getBilliardsOpenStatus(getApiPlayVo.getBilliardsId())){
            throw new BaseException(BaseErrorEnum.OPEN_ERROR);
        }

        String out_trade_no = PayKit.generateStr();


        Goods goods =  goodsService.selectGoods(getApiPlayVo.getGoodsId());

        addOrder(out_trade_no,getApiPlayVo,goods);

        int targetAmount = typeToPrice(goods);



        try {
            String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                    .setAppid(wxPayV3Bean.getAppId())
                    .setMchid(wxPayV3Bean.getMchId())
                    .setDescription("IJPay 让支付触手可及")
                    .setOut_trade_no(out_trade_no)
                    .setTime_expire(timeExpire)
                    .setAttach("台球")
                    .setNotify_url("https://kygtq.cn/payNotify")
                    .setAmount(new Amount().setTotal(targetAmount))
                    .setPayer(new Payer().setOpenid(getApiPlayVo.getOpenId()));

            //http://124.221.253.69/
            //https://30jp902554.yicp.fun/
            //  targetAmount
            // https://ycenjoy.com/payNotify
            // http://124.221.253.69/payNotify

            log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    BasePayApiEnum.JS_API_PAY.toString(),
                    wxPayV3Bean.getMchId(),
                    getSerialNumber(),
                    null,
                    wxPayV3Bean.getKeyPath(),
                    JSONUtil.toJsonStr(unifiedOrderModel)
            );

            log.info("统一下单响应 {}", response);
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wxPayV3Bean.getPlatformCertPath());
            log.info("verifySignature: {}", verifySignature);
            System.out.println(response.getStatus());

//            if (billiardsService.getBilliardsOpenStatus(getApiPlayVo.getBilliardsId())){
//                throw new BaseException(BaseErrorEnum.OPEN_ERROR);
//            }


            if (response.getStatus() == 200 && verifySignature) {

                String body = response.getBody();
                cn.hutool.json.JSONObject jsonObject = JSONUtil.parseObj(body);
                String prepayId = jsonObject.getStr("prepay_id");
                Map<String, String> map = WxPayKit.jsApiCreateSign(wxPayV3Bean.getAppId(), prepayId, wxPayV3Bean.getKeyPath());
                log.info("唤起支付参数:{}", map);

                return RespGenerator.returnOK(map);

            }

            System.out.println(JSONUtil.toJsonStr(response));
            return RespGenerator.returnOK(response);

        } catch (Exception e) {
            e.printStackTrace();
            return RespGenerator.returnOK(e.getMessage());

        }

    }

    @PostMapping("/payNotify")
    @ResponseBody
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {

        Map<String, String> map = new HashMap<>(12);
        System.out.println("进入");

        System.out.println("payNotifypayNotify");

        try {

            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpKit.readData(request);
            log.info("支付通知密文 {}", result);

            // 需要通过证书序列号查找对应的证书，verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                    wxPayV3Bean.getApiKey3(), wxPayV3Bean.getPlatformCertPath());
            log.info("支付通知明文 {}", plainText);
            JSONObject jsonObject = JSONObject.parseObject(plainText);
            String out_trade_no =  jsonObject.getString("out_trade_no");
            String transaction_id =  jsonObject.getString("transaction_id");



//           支付通知明文 {"mchid":"1634897734","appid":"wxabcaaa75fc3c451a","out_trade_no":"dd871cb91b6b4f2ab267e386c1f424bc",
//                "transaction_id":"4200001797202303274932470425","trade_type":"JSAPI","trade_state":"SUCCESS",
//                "trade_state_desc":"支付成功","bank_type":"OTHERS","attach":"微信系开发脚手架 https://gitee.com/javen205/TNWX",
//                "success_time":"2023-03-27T13:29:35+08:00","payer":{"openisavePlatformCertd":"o_4C64mEUlC7ew8RkEboRNduJFlk"},
//            "amount":{"total":2,"payer_total":2,"currency":"CNY","payer_currency":"CNY"}}
            System.out.println("jsonObject");
            System.out.println(jsonObject);
            System.out.println( jsonObject.getString("attach"));


            //transaction_id

            if (StrUtil.isNotEmpty(plainText)) {
                response.setStatus(200);

                if (jsonObject.getString("attach").equals("台球")){

                    orderService.updateOrderStatus(out_trade_no);

                }else if (jsonObject.getString("attach").equals("充值")){

                    System.out.println("dasdasdasdasd");
                    orderService.updateOrderPayStatus(out_trade_no);

                }

                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");

            } else {
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
            }
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    private String getSerialNumber() {
        if (StrUtil.isEmpty(serialNo)) {
            // 获取证书序列号
            X509Certificate certificate = PayKit.getCertificate(wxPayV3Bean.getCertPath());
            if (null != certificate) {
                serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
                // 提前两天检查证书是否有效
                boolean isValid = PayKit.checkCertificateIsValid(certificate, wxPayV3Bean.getMchId(), -2);
                //    log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
            }

//            System.out.println("输出证书信息:\n" + certificate.toString());
//            // 输出关键信息，截取部分并进行标记
//            System.out.println("证书序列号:" + certificate.getSerialNumber().toString(16));
//            System.out.println("版本号:" + certificate.getVersion());
//            System.out.println("签发者：" + certificate.getIssuerDN());
//            System.out.println("有效起始日期：" + certificate.getNotBefore());
//            System.out.println("有效终止日期：" + certificate.getNotAfter());
//            System.out.println("主体名：" + certificate.getSubjectDN());
//            System.out.println("签名算法：" + certificate.getSigAlgName());
//            System.out.println("签名：" + certificate.getSignature().toString());
        }
        System.out.println("serialNo:" + serialNo);
        return serialNo;
    }





}
